﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace GameStateManagement
{
    public class Missile : Microsoft.Xna.Framework.DrawableGameComponent
    {

        public Matrix worldTransformMatrix;
        protected float fScale;
        protected Vector3 vWorldPosition;
        protected Quaternion qRotation;
        protected bool hasChanged;

        protected Model actorModel;
        protected ContentManager contentManager;
        protected Matrix[] actorBones;
        protected Utils.Timer timers;
        protected Game game;

        protected Vector3 vVelocity;
        protected float fMass;
        protected float fTerminalVelocity;
        protected Vector3 vForce;
        protected Vector3 vAccerlation;
        protected bool bPhysicsDriven;
        public bool followCam;

        public static String meshName;

        public BoundingSphere ModelBounds;
        public BoundingSphere WorldBounds;

        public Missile(Game game)
            : base(game)
        {
            this.game = game;
            meshName = "Missile";
        }
                
        public override void Initialize()
        {

            this.fMass = 10.0f;
            this.bPhysicsDriven = true;
            this.fTerminalVelocity = 550.0f;

            timers = new Utils.Timer();
            worldTransformMatrix = Matrix.Identity;

            fScale = 1.0f;
            vWorldPosition = new Vector3(0.0f, 0.0f, 0.0f);
            qRotation = Quaternion.Identity;
            hasChanged = false;

            
            vForce = new Vector3(0.0f, 0.0f, 0.0f);
            vAccerlation = new Vector3(0.0f, 0.0f, 0.0f);
            vVelocity = new Vector3(0.0f, 0.0f, 0.0f);

            followCam = true;
            base.Initialize();
        }

        public void switchCam()
        {
            followCam = !followCam;
        }

        public void updateRotation()
        {

            
            float fRadius = this.Position.Length();

            float fyAngle = (float)Math.Acos(this.Position.Y / fRadius);
            float fzAngle = (float)Math.Asin(this.Position.X / Math.Sqrt(Math.Pow(this.Position.X, 2) + Math.Pow(this.Position.Z, 2)));
            if (this.Position.Z < 0)
            {
                fzAngle = (float)Math.PI - fzAngle;
            }

            Quaternion siderot = Quaternion.CreateFromAxisAngle(Vector3.UnitY, fzAngle);
            Quaternion uprot = Quaternion.CreateFromAxisAngle(-Vector3.UnitX, (float)(Math.PI / 2.0) - fyAngle);

            this.Rotation = siderot * uprot;

            updateWorldTransformMatrix();
            

        }

        public void updateRotation(Vector3 converge)
        {
            Vector3 tempPos = converge + this.Position;

            float fRadius = tempPos.Length();


            float fyAngle = (float)Math.Acos(tempPos.Y / fRadius);
            float fzAngle = (float)Math.Asin(tempPos.X / Math.Sqrt(Math.Pow(tempPos.X, 2) + Math.Pow(tempPos.Z, 2)));
            if (tempPos.Z < 0)
            {
                fzAngle = (float)Math.PI - fzAngle;
            }

            Quaternion siderot = Quaternion.CreateFromAxisAngle(Vector3.UnitY, fzAngle);
            Quaternion uprot = Quaternion.CreateFromAxisAngle(-Vector3.UnitX, (float)(Math.PI / 2.0) - fyAngle);

            this.Rotation = siderot * uprot;

            updateWorldTransformMatrix();


        }

        public Matrix getWorldTransformMatrix()
        {
            return worldTransformMatrix;
        }
        public void setWorldTransformMatrix(Matrix world)
        {
            worldTransformMatrix = world;
        }

        public float Scale
        {
            get { return fScale; }
            set
            {
                fScale = value;
                hasChanged = true;
            }
        }

        public Vector3 Position
        {
            get { return vWorldPosition; }
            set
            {
                vWorldPosition = value;
                hasChanged = true;
            }
        }

        public Quaternion Rotation
        {
            get { return qRotation; }
            set { qRotation = value; } 
        }

        public Vector3 Velocity
        {
            get { return new Vector3(vVelocity.X, vVelocity.Y, vVelocity.Z); }
            set { vVelocity = value; }
        }

        public Vector3 Force
        {
            get { return vForce; }
            set { vForce = value; }
        }

        public Vector3 Acceleration
        {
            get { return vAccerlation; }
            set { vAccerlation = value; }
        }

        public void updateWorldTransformMatrix()
        {
            hasChanged = false;

            Matrix scaleMatrix = Matrix.CreateScale(fScale);
            Matrix rotationMatrix = Matrix.CreateFromQuaternion(qRotation);
            Matrix translationMatrix = Matrix.CreateTranslation(vWorldPosition);

            worldTransformMatrix = scaleMatrix;
            worldTransformMatrix *= rotationMatrix;
            worldTransformMatrix *= translationMatrix;

            WorldBounds.Center = vWorldPosition;
            WorldBounds.Radius = ModelBounds.Radius * fScale;
        }

        public override void Update(GameTime gameTime)
        {
            float dTime = ((float)gameTime.ElapsedGameTime.Ticks / System.TimeSpan.TicksPerMillisecond / 1000.0f);


            if (bPhysicsDriven)
            {
                this.Velocity = vVelocity + (vAccerlation * dTime / 2.0f);
                if (vVelocity.Length() > fTerminalVelocity)
                {
                    vVelocity.Normalize();
                    this.Velocity = vVelocity * fTerminalVelocity;
                }
                this.Position = vWorldPosition + (dTime * vVelocity);
                vAccerlation = vForce / fMass;
                this.Velocity = vVelocity + (vAccerlation * dTime / 2.0f);
            }
            else
            {
                this.Position = vWorldPosition + (dTime * vVelocity);
            }



            timers.Update(gameTime);
            base.Update(gameTime);
        }

        protected override void LoadContent()
        {
            contentManager = new ContentManager(Game.Services, "Content");
            actorModel = contentManager.Load<Model>(meshName);
            actorBones = new Matrix[actorModel.Bones.Count];


            base.LoadContent();

            foreach (ModelMesh mesh in actorModel.Meshes)
            {
                ModelBounds = BoundingSphere.CreateMerged(ModelBounds,
                mesh.BoundingSphere);
            }

        }

        protected override void UnloadContent()
        {
            contentManager.Unload();
            base.UnloadContent();
        }

        public override void Draw(GameTime gameTime)
        {
            if (hasChanged)
            {
                updateWorldTransformMatrix();
            }

            GraphicsDevice.RenderState.DepthBufferEnable = true;
            actorModel.CopyAbsoluteBoneTransformsTo(actorBones);
            foreach (ModelMesh mesh in actorModel.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.World =
                    actorBones[mesh.ParentBone.Index] * worldTransformMatrix;
                    if (followCam)
                    {
                        effect.View = GameplayScreen.camera.View;
                        effect.Projection = GameplayScreen.camera.Projection;
                    }
                    else
                    {
                        effect.View = GameplayScreen.CameraMatrix;
                        effect.Projection = GameplayScreen.ProjectionMatrix;
                    }
                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;
                    effect.AmbientLightColor = GameplayScreen.ambientLightColor;
                    effect.DirectionalLight0.DiffuseColor = GameplayScreen.diffuseColor;
                    effect.DirectionalLight0.Direction = GameplayScreen.diffuseDirection;
                    effect.SpecularColor = GameplayScreen.specularColor;
                    effect.SpecularPower = GameplayScreen.specularPower;

                }
                mesh.Draw();
            }
            base.Draw(gameTime);
        }


    }
}
